/****************************************************************
 * <strong>MFMVCReflect</strong>
 *
 * <p>
 * MFMVCReflect
 * </p>
 *
 * @author AndyZhang 2013
 * @version 0.1
 * ToDo:2013-3-3
 ***************************************************************/
package com.thinkwide.data.mvc.config;

import com.thinkwide.data.mvc.annotation.MFRequestBodyAttr;
import com.thinkwide.data.mvc.annotation.MFRequestMappingAttr;
import com.thinkwide.data.mvc.annotation.MFRequestParamAttr;
import com.thinkwide.data.mvc.annotation.MFResponseBodyAttr;
import com.thinkwide.data.ormapping.MFReflect;
import com.thinkwide.data.mvc.core.MFClassFind;
import com.thinkwide.data.mvc.core.MFMVCReturnType;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.EnumMemberValue;
import javassist.bytecode.annotation.MemberValue;
import javassist.bytecode.annotation.StringMemberValue;
import org.json.JSONArray;
import org.json.JSONObject;

import java.util.List;
import java.util.Map;

public class MFMVCReflect {

    public static void scanClassList(MFMappingItems items, String path) throws Exception {
        List<Class<?>> list = MFClassFind.getClassList(path);
        for (Class item : list) {
            setMappings(items, item);
        }
    }

    public static void setMappings(MFMappingItems items, Class clazz) throws NotFoundException, ClassNotFoundException {
        ClassPool pool = ClassPool.getDefault();

        pool.insertClassPath(new ClassClassPath(clazz));
        CtClass ctClass = pool.getCtClass(clazz.getName());

        //Method[] methods = clazz.getDeclaredMethods();
        CtMethod[] methods = ctClass.getDeclaredMethods();
        for (CtMethod m : methods) {
            MFMappingItem item = getParamName(clazz, m);
            if (item != null) items.add(item);
        }
    }

    public static MFMappingItem getParamName(Class clazz, CtMethod method) throws ClassNotFoundException {
        try {
            MFMappingItem pItem = new MFMappingItem();

            ClassPool pool = ClassPool.getDefault();
            CtClass cc = pool.get(clazz.getName());
            CtMethod cm = method;//cc.getDeclaredMethod(method,);

            // 方法相关

            pItem.setClassName(clazz.getName());
            pItem.setMethodName(cm.getName());

            MethodInfo methodInfo = cm.getMethodInfo();
            CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
            LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
            if (attr == null) {
            }
            ;

            AnnotationsAttribute atrr = (AnnotationsAttribute) methodInfo.getAttribute(AnnotationsAttribute.visibleTag);
            String url = "";
            if (atrr.length() > 0) {
                //Annotation annotation = atrr.getAnnotations()[0];
                Annotation annotation = atrr.getAnnotation(MFRequestMappingAttr.class.getName());
                if (annotation != null) {
                    MemberValue mv = annotation.getMemberValue("value");
                    if (mv != null) url = ((StringMemberValue) mv).getValue();
                    //MFDebug.println(url);
                }

                //MFDebug.println(annotation.getTypeName());

                annotation = atrr.getAnnotation(MFResponseBodyAttr.class.getName());
                if (annotation != null) {
                    MemberValue mv = annotation.getMemberValue("value");
                    pItem.setReturnType(MFMVCReturnType.Text);
                    if (mv != null) {
                        String data = ((EnumMemberValue) mv).getValue();
                        MFMVCReturnType tp = MFMVCReturnType.valueOf(data);
                        pItem.setReturnType(tp);
                    }
                    //MFDebug.println(data);
                }

            } else {
                return null;
            }

            pItem.setUrl(url);

            //方法相关

            //参数相关

            CtClass[] parameterTypes = cm.getParameterTypes();

            Object[][] list = cm.getParameterAnnotations();

            String[] paramNames = new String[cm.getParameterTypes().length];

            int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
            for (int i = 0; i < paramNames.length; i++)
                paramNames[i] = attr.variableName(i + pos);
            // paramNames即参数名
            for (int i = 0; i < paramNames.length; i++) {
                MFXParam param = new MFXParam();
                param.setParamName(paramNames[i]);
                param.setParamType(parameterTypes[i].getName());

                for (Object item : list[i]) {
                    if (item instanceof MFRequestParamAttr) {
                        MFRequestParamAttr ParamAttr = (MFRequestParamAttr) item;
                        System.out.println(ParamAttr.value());
                        param.setRequired(ParamAttr.required());
                        param.setValue(ParamAttr.value());
                        param.setDefaultValue(ParamAttr.defaultValue());
                    }

                    if (item instanceof MFRequestBodyAttr) {
                        MFRequestBodyAttr ParamAttr = (MFRequestBodyAttr) item;
                        System.out.println(ParamAttr.value().toString());

                        param.setConvertClass(ParamAttr.convertClass());
                        param.setInputType(ParamAttr.value());
                    }
                }


                //System.out.println(parameterTypes[i].getName());
                //System.out.println(paramNames[i]);

                pItem.getParams().add(param);

            }

            return pItem;
            //参数相关
        } catch (NotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static <T> JSONObject getEntityToJson(T Entity) throws Throwable {
        List<String> ls = MFReflect.getPropertyList(Entity.getClass());

        JSONObject obj = new JSONObject();

        for (String name : ls) {

            obj.put(name, MFReflect.getProperty(Entity, name));
        }

        return obj;
    }

    public static <T> JSONArray getEntityListToJson(List<T> EntityList) throws Throwable {
        JSONArray array = new JSONArray();
        for (T item : EntityList) {
            JSONObject obj = getEntityToJson(item);
            array.put(obj);
        }

        return array;
    }

    public static <T> JSONObject getMapToJson(Map<String, Object> map) throws Throwable {
        JSONObject obj = new JSONObject();

        for (Map.Entry<String, Object> item : map.entrySet()) {
            obj.put(item.getKey(), item.getValue());
        }

        return obj;
    }
}
